#pragma once

#include <random>
#include "message.hpp"
#include "p2pms_log.hpp"
#include "call_agent_interface.hpp"


#include "rtc_base/thread.h"
#include "api/peer_connection_interface.h"

typedef rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> IPcFactoryPtr;
typedef rtc::scoped_refptr<webrtc::PeerConnectionInterface> IPeerConnectionPtr;

namespace p2pms
{


//--------------------------------------------------------------------------------------------------
// 
//--------------------------------------------------------------------------------------------------
template <typename T>
T SyncProxy(rtc::Thread* thread, std::function<T()> func) 
{
	if (thread->IsCurrent()) {
		return func();
	}

	T result;
	rtc::Event event;
	thread->PostTask([&]() {
		result = func();
		event.Set();
	});
	event.Wait(rtc::Event::kForever);
	return result;
}

//--------------------------------------------------------------------------------------------------
// Specialize
//--------------------------------------------------------------------------------------------------
void SyncProxy(rtc::Thread* thread, std::function<void()> func);

//--------------------------------------------------------------------------------------------------
// 
//--------------------------------------------------------------------------------------------------
template<typename T>
bool ParseRequest(const Message& msg, T& data)
{
	if (msg.data.is_null()) {
		LOG_ERR("Invalid request data");
		return false;
	}

	if (!msg.data.is_object()) {
		LOG_ERR("Message data is not object");
		return false;
	}

	try {
		data = msg.data.get<T>();
	}
	catch (const std::exception& e) {
		LOG_ERR("Failed to parse reqeust data: {}", e.what());
		return false;
	}

	return true;
}

//--------------------------------------------------------------------------------------------------
// 
//--------------------------------------------------------------------------------------------------
template<typename T>
bool ParseResponse(const Message& msg, T& data, int& code)
{
	if (msg.data.is_null()) {
		LOG_ERR("Invalid request data");
		return false;
	}

	if (!msg.data.is_object()) {
		LOG_ERR("Message data is not object");
		return false;
	}

	ResMsgData msg_data;
	try {
		msg_data = msg.data.get<ResMsgData>();
	}
	catch (const std::exception& e) {
		LOG_ERR("Failed to parse ResMsgData: {}", e.what());
		return false;
	}

	code = msg_data.code;

	try {
		data = msg_data.data.get<T>();
	}
	catch (const std::exception& e) {
		LOG_ERR("Failed to parse response data: {}", e.what());
		return false;
	}

	return true;
}

////////////////////////////////////////////////////////////////////////////////////////////////////


std::string GenerateRandomStr(size_t length = 8);
std::string ToString(const MediaInfo& media);
std::string ToString(CallState state);

bool ValidateMediaInfo(const MediaInfo& media);
std::string GenerateUUID();
bool IsP2PCall(CallMode mode);


////////////////////////////////////////////////////////////////////////////////////////////////////

Message MakeCommonResMsg(
	const std::string& cmd,
	const std::string& rid, 
	int code, 
	const std::string& msg,
	const nlohmann::json& data = nlohmann::json{});
Message MakeLoginReqMsg(
	const std::string& user_id);
Message MakeLoginAckMsg(
	const std::string& rid,
	const std::string& user_id);
Message MakeCallMakeReqMsg(
	const std::string& call_type,
	const std::string& caller_id,
	const std::string& callee_id);
Message MakeCallMakeAckMsg(
	const std::string& rid, 
	int code,
	const std::string& msg,
	const std::string& call_type,
	const std::string& call_id,
	const std::string& caller_id,
	const std::string& callee_id);
Message MakeP2PCandidateNotifyMsg(
	const std::string& call_id,
	const std::string& user_id,
	const std::string& sdp_mid,
	int sdp_mline_index,
	const std::string& candidate);
Message MakeP2PNegotiateReqMsg(
	const std::string& call_id,
	const std::string& user_id,
	const std::string& sdp);
Message MakeP2PNegotiateResMsg(
	const std::string& rid,
	int code,
	const std::string& msg,
	const std::string& call_id,
	const std::string& user_id,
	const std::string& sdp);
Message MakeCallEndReqMsg(
	const std::string& call_id,
	const std::string& user_id);
Message MakeCallEndAckMsg(
	const std::string& rid, 
	int code,
	const std::string& msg,
	const std::string& call_id,
	const std::string& user_id);
Message MakeP2PStartPublishReqMsg(
	const std::string& call_id,
	const std::string& user_id,
	const MediaInfo& media);
Message MakeP2PStopPublishReqMsg(
	const std::string& call_id,
	const std::string& user_id,
	const MediaInfo& media);
Message MakeP2PStopPublishAckMsg(
	const std::string& rid, 
	int code,
	const std::string& msg,
	const std::string& call_id,
	const std::string& user_id,
	const MediaInfo& media);
Message MakeP2PStartSubscribeReqMsg(
	const std::string& call_id,
	const std::string& user_id,
	const std::string& pub_user_id,
	const MediaInfo& media);
Message MakeP2PStopSubscribeReqMsg(
	const std::string& call_id,
	const std::string& user_id,
	const std::string& pub_user_id,
	const MediaInfo& media);
Message MakeP2PStartProduceAckMsg(
	const std::string& rid, 
	int code, 
	const std::string& msg,
	const std::string& call_id,
	const std::string& user_id);

// SFU message constructors
Message MakeSFUPrepareRoomReqMsg(
	const std::string& user_id,
	const std::string& room_id);
Message MakeSFUJoinRoomReqMsg(
	const std::string& room_id,
	const std::string& user_id,
	const nlohmann::json& rtp_caps,
	const nlohmann::json& sctp_caps);
Message MakeSFULeaveRoomReqMsg(
	const std::string& room_id,
	const std::string& user_id);
Message MakeSFUCreateTransportReqMsg(
	const std::string& room_id,
	const std::string& user_id,
	const std::string& transport_type,
	bool force_tcp,
	bool producing,
	bool consuming,
	const nlohmann::json& sctp_caps);
Message MakeSFUConnectTransportReqMsg(
	const std::string& room_id, 
	const std::string& user_id,
	const std::string& transport_type, 
	const std::string& transport_id,
	const std::string& send_or_recv, 
	const nlohmann::json& dtls_params);
Message MakeSFUStartProduceReqMsg(
	const std::string& room_id, 
	const std::string& user_id,
	const MediaInfo& media,
	const std::string& transport_id,
	const std::string& kind,
	const nlohmann::json& rtp_params,
	const nlohmann::json& app_data);
Message MakeSFUStopProduceReqMsg(
	const std::string& room_id, 
	const std::string& user_id,
	const MediaInfo& media,
	const std::string& producer_id);
Message MakeSFUStartPublishReqMsg(
	const std::string& room_id,
	const std::string& user_id,
	const MediaInfo& media);
Message MakeSFUStartPublishAckMsg(
	const std::string& rid,
	const std::string& room_id,
	const std::string& user_id,
	const MediaInfo& media);
Message MakeSFUStopPublishAckMsg(
	const std::string& rid,
	const std::string& room_id,
	const std::string& user_id,
	const MediaInfo& media);
Message MakeSFUStartSubscribeReqMsg(
	const std::string& room_id,
	const std::string& sub_user_id,
	const std::string& pub_user_id,
	const MediaInfo& media);
Message MakeSFUStopPublishReqMsg(
	const std::string& room_id,
	const std::string& user_id,
	const MediaInfo& media);
Message MakeSFUStopSubscribeReqMsg(
	const std::string& room_id,
	const std::string& user_id,
	const MediaInfo& media);

} // namespace p2pms